package org.particlethink.service.reconciliation;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.particlethink.auth.DefaultUser;
import org.particlethink.context.Context;
import org.particlethink.context.DefaultContext;
import org.particlethink.entry.BillStatus;
import org.particlethink.entry.DeliverStatus;
import org.particlethink.entry.PlanTyp;
import org.particlethink.exception.NoPermissionException;
import org.particlethink.exception.StatusException;
import org.particlethink.model.*;
import org.particlethink.response.*;
import org.particlethink.utils.ListUtilsKt;
import org.particlethink.utils.ObjectUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service("ProjectUserImpl")
public class ProjectUserImpl extends Base {//项目用户


    //todo 选择发货单只能选择 Warehoused 状态的

    @Transactional
    public void createBill(Bill bill, List<Integer> deliverIds) {
        List<Deliver> delivers = this.deliverMapper.queryDelivers(deliverIds, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
        deliverIds = delivers.stream().filter(Deliver::canCreateBill).map(Deliver::getId).collect(Collectors.toList());
        if (deliverIds.isEmpty()) {
            return;
        }
        this.billMapper.insert(bill);
        this.deliverMapper.UpdateBillIdAndAtByIDs(deliverIds, bill.getId(), new Date());
//        更新发货单状态
        this.deliverMapper.UpdateStatusByIDs(deliverIds, DeliverStatus.Reconciled);
    }


    //撤回对账单 作废
    public void discardBill(int billID) {
        Bill bill = this.billMapper.selectByPrimaryKey(billID);
        if (!bill.canDiscard()) {
            throw new StatusException();
        }


        bill.setStatus(BillStatus.Discard);
        this.billMapper.updateByPrimaryKey(bill);

        //        更新发货单状态
        List<Deliver> delivers = this.deliverMapper.selectByBillID(billID);
        List<Integer> deliverIDs = delivers.stream().map(Deliver::getId).collect(Collectors.toList());
        this.deliverMapper.UpdateStatusByIDs(deliverIDs, DeliverStatus.Warehoused);
    }

    //    重新发起对账单
    public void reCreateBill(List<Integer> projectIds, int billID) {
        Bill bill = this.billMapper.selectByPrimaryKey(billID);
        if (!bill.canReCreate()) {
            throw new StatusException();
        }
        if (!projectIds.contains(bill.getProjectId())) {
            throw new NoPermissionException();
        }

        bill.setStatus(BillStatus.UnConfirmed);
        this.billMapper.updateByPrimaryKey(bill);
    }

//    public void recallBill(int billID) {
//        Bill bill = this.billMapper.selectByPrimaryKey(billID);
//        if (!bill.canRecall()) {
//            throw new StatusException();
//        }
//        bill.setStatus(BillStatus.UnConfirmed);
//        this.billMapper.updateByPrimaryKeySelective(bill);
//    }


    public PageInfo<BillResponse> billList(User user, String managementLevel, Integer projectId, String supplierName, String creatorName, BillStatus status, Date billAt, Date createStart, Date createEnd, String searchKey, Integer pageNum, Integer pageSize) {
        if (pageNum == null) {
            pageNum = 1;
        }
        if (pageSize == null) {
            pageSize = 20;
        }

        List<Project> projects = null;
        if (!ObjectUtils.isEmpty(projectId)) {
            ArrayList<Integer> projectIds = new ArrayList<>();
            projectIds.add(projectId);

            projects = this.projectMapper.selectByIds(user.getToken(), projectIds);
        } else {
            projects = this.projectMapper.list(user.getToken(), "", managementLevel, user.getProjectIds());
        }

        if (searchKey != null && !searchKey.isEmpty()) {
            List<Project> newProjects = projects.stream().filter(project -> {
                return project.getName().contains(searchKey);
            }).collect(Collectors.toList());

            if (!newProjects.isEmpty()) {
                projects = newProjects;
            } else {
                return new PageInfo<>(new ArrayList<>());
            }
        }


        List<Supplier> suppliers = this.supplierMapper.SelectByName(user.getToken(), supplierName);
        List<Integer> supplierIds = user.getSupplierIds();
        if (supplierIds != null && !supplierIds.isEmpty()) {
            suppliers = suppliers.stream().filter(supplier -> supplierIds.contains(supplier.getId())).collect(Collectors.toList());
        }

        List<ProjectUser> projectUsers;
        List<Integer> creatorIds = null;
        if (creatorName != null && !creatorName.isEmpty()) {
            projectUsers = projectUserMapper.selectByName(creatorName);
            creatorIds = projectUsers.stream().map(ProjectUser::getId).collect(Collectors.toList());
            if (creatorIds.isEmpty()) {
                return new PageInfo<>();
            }
        } else {
            projectUsers = projectUserMapper.selectAll();
        }

        PageHelper.startPage(pageNum, pageSize);
        PageInfo<Bill> bills = new PageInfo<>(this.billMapper.queryBills(projects.stream().map(Project::getId).collect(Collectors.toList()), suppliers.stream().map(Supplier::getId).collect(Collectors.toList()), creatorIds, status, billAt, createStart, createEnd));

        Map<Integer, Project> projectMap = ListUtilsKt.map(projects, Project::getId);


        Map<Integer, ProjectUser> projectUserMap = ListUtilsKt.map(projectUsers, ProjectUser::getId);

        Map<Integer, Supplier> supplierMap = ListUtilsKt.map(suppliers, Supplier::getId);

        return bills.convert(bill -> {
            return new BillResponse(bill, projectMap.get(bill.getProjectId()), supplierMap.get(bill.getSupplierId()), projectUserMap.get(bill.getCreatorId()));
        });
    }

    public PageInfo<BillResponse> queryNo(User user, String no, BillStatus status) {
        List<Bill> bills = this.billMapper.queryNo(no, status);
        if (bills == null || bills.isEmpty()) {
            return new PageInfo<>(new ArrayList<>());
        }
        Context ctx = DefaultContext.Builder.background().withValue("token", ((DefaultUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUser().getToken());


        List<Integer> projectIds = bills.stream().map(Bill::getProjectId).distinct().collect(Collectors.toList());
        List<Project> projects = this.projectMapper.selectByIds(user.getToken(), projectIds);
        Map<Integer, Project> projectMap = ListUtilsKt.map(projects, Project::getId);

        List<Integer> supplierIds = bills.stream().map(Bill::getSupplierId).distinct().collect(Collectors.toList());
        List<Supplier> suppliers = this.supplierMapper.selectByIds(user.getToken(), supplierIds);
        Map<Integer, Supplier> supplierMap = ListUtilsKt.map(suppliers, Supplier::getId);

        List<Integer> creatorIds = bills.stream().map(Bill::getCreatorId).distinct().collect(Collectors.toList());
        List<ProjectUser> projectUsers = this.projectUserMapper.selectByIds(creatorIds);
        Map<Integer, ProjectUser> projectUserMap = ListUtilsKt.map(projectUsers, ProjectUser::getId);


        List<BillResponse> billResponses = bills.stream().map(bill -> {
            return new BillResponse(bill, projectMap.get(bill.getProjectId()), supplierMap.get(bill.getSupplierId()), projectUserMap.get(bill.getCreatorId()));
        }).collect(Collectors.toList());
        return new PageInfo<>(billResponses);
    }


    public List<BillExportResponse> exportBill(User user, String managementLevel, Integer projectId, String supplierName, String creatorName, BillStatus status, Date billAt, Date createStart, Date createEnd) {

        List<Project> projects = null;
        if (!ObjectUtils.isEmpty(projectId)) {
            ArrayList<Integer> projectIds = new ArrayList<>();
            projectIds.add(projectId);

            projects = this.projectMapper.selectByIds(user.getToken(), projectIds);
        } else {
            projects = this.projectMapper.list(user.getToken(), "", managementLevel, user.getProjectIds());
        }

        List<Supplier> suppliers = this.supplierMapper.SelectByName(user.getToken(), supplierName);
        List<ProjectUser> projectUsers;
        List<Integer> creatorIds = null;
        if (creatorName != null && !creatorName.isEmpty()) {
            projectUsers = projectUserMapper.selectByName(creatorName);
            creatorIds = projectUsers.stream().map(ProjectUser::getId).collect(Collectors.toList());
        } else {
            projectUsers = projectUserMapper.selectAll();
        }

        List<Bill> bills = this.billMapper.queryBills(projects.stream().map(Project::getId).collect(Collectors.toList()), suppliers.stream().map(Supplier::getId).collect(Collectors.toList()), creatorIds, status, billAt, createStart, createEnd);

        Map<Integer, Project> projectMap = ListUtilsKt.map(projects, Project::getId);


        Map<Integer, ProjectUser> projectUserMap = ListUtilsKt.map(projectUsers, ProjectUser::getId);

        Map<Integer, Supplier> supplierMap = ListUtilsKt.map(suppliers, Supplier::getId);

        return bills.stream().map(bill -> {
            return new BillExportResponse(bill, projectMap.get(bill.getProjectId()), supplierMap.get(bill.getSupplierId()), projectUserMap.get(bill.getCreatorId()));
        }).collect(Collectors.toList());
    }


    public BillDetailResponse billDetail(User user, Integer id) {


        Bill bill = this.billMapper.selectByPrimaryKey(id);


        Project project = this.projectMapper.selectById(user.getToken(), bill.getProjectId());
        Supplier supplier = this.supplierMapper.selectById(user.getToken(), bill.getSupplierId());
        ProjectUser projectUser = this.projectUserMapper.selectByPrimaryKey(bill.getCreatorId());
        BillResponse billResponse = new BillResponse(bill, project, supplier, projectUser);


        List<Deliver> delivers = this.deliverMapper.selectByBillId(id);
        Map<Integer, Deliver> deliverMap = ListUtilsKt.map(delivers, Deliver::getId);

        List<Integer> deliverIds = delivers.stream().map(Deliver::getId).collect(Collectors.toList());

        List<PlanMaterial> planMaterials = this.planMaterialMapper.selectByPlanIDsAndTyp(deliverIds, PlanTyp.Deliver);

        List<Integer> materialIds = planMaterials.stream().map(PlanMaterial::getMaterialId).collect(Collectors.toList());
        List<Material> materials = this.materialMapper.selectByIds(user.getToken(), materialIds.stream().distinct().collect(Collectors.toList()));
        Map<Integer, Material> materialMap = ListUtilsKt.map(materials, Material::getId);


        List<Acceptance> acceptances = this.acceptanceMapper.selectByDeliverIds(deliverIds);
        Map<Integer, Acceptance> acceptanceMap = ListUtilsKt.map(acceptances, Acceptance::getDeliverId);

        List<BillDeliverResponse> billDeliverResponses = planMaterials.stream().map(planMaterial -> {
            Deliver deliver = deliverMap.get(planMaterial.getPlanId());
            return new BillDeliverResponse(
                    planMaterial,
                    materialMap.get(planMaterial.getMaterialId()),
                    deliver,
                    acceptanceMap.get(deliver.getId())
            );
        }).collect(Collectors.toList());


        return new BillDetailResponse(billResponse, billDeliverResponses);
    }

    public BillDownloadResponse download(User user, Integer id) {
        Bill bill = this.billMapper.selectByPrimaryKey(id);
        Project project = this.projectMapper.selectById(user.getToken(), bill.getProjectId());
        Supplier supplier = this.supplierMapper.selectById(user.getToken(), bill.getSupplierId());

        List<Deliver> delivers = this.deliverMapper.selectByBillId(id);
        Map<Integer, Deliver> deliverMap = ListUtilsKt.map(delivers, Deliver::getId);

        List<Integer> deliverIds = delivers.stream().map(Deliver::getId).collect(Collectors.toList());

        List<PlanMaterial> planMaterials = this.planMaterialMapper.selectByPlanIDsAndTyp(deliverIds, PlanTyp.Deliver);

        List<Integer> materialIds = planMaterials.stream().map(PlanMaterial::getMaterialId).collect(Collectors.toList());
        List<Material> materials = this.materialMapper.selectByIds(user.getToken(), materialIds.stream().distinct().collect(Collectors.toList()));
        Map<Integer, Material> materialMap = ListUtilsKt.map(materials, Material::getId);


        List<Acceptance> acceptances = this.acceptanceMapper.selectByDeliverIds(deliverIds);
        Map<Integer, Acceptance> acceptanceMap = ListUtilsKt.map(acceptances, Acceptance::getDeliverId);

        List<BillDownloadMaterialResponse> billDeliverResponses = planMaterials.stream().map(planMaterial -> {
            Deliver deliver = deliverMap.get(planMaterial.getPlanId());
            return new BillDownloadMaterialResponse(
                    planMaterial,
                    materialMap.get(planMaterial.getMaterialId()),
                    deliver,
                    acceptanceMap.get(deliver.getId())
            );
        }).collect(Collectors.toList());


        return new BillDownloadResponse(
                project.getName(),
                supplier.getName(),
                bill.getStartAt(),
                bill.getEndAt(),
                bill.getDeductionAmount(),
                bill.getDeductionReason(),
                bill.getRewardAmount(),
                bill.getRewardReason(),
                billDeliverResponses
        );

    }
}
